package org.web3j.crypto;

import java.io.IOException;
import java.math.BigInteger;

import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.dsa.DSASigner.stdDSA;
import org.bouncycastle.math.ec.ECPoint;
import org.web3j.crypto.Sign.SignatureData;
import org.web3j.crypto.gm.SM2Signature;
import org.web3j.crypto.gm.SM2Signer;
import org.web3j.crypto.gm.StandardDSAEncoding;

public final class Crypto {

	public static final StandardDSAEncoding STANDARD_DSA_ENCODING = new StandardDSAEncoding();

	public static SM2Signature sm2Signature(BigInteger privateKey, byte[] transactionHash) {
		SM2Signer signer = new SM2Signer(new SM3Digest());
		ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(privateKey, Sign.CURVE);
		signer.init(true, privKey);
		signer.update(transactionHash, 0, transactionHash.length);

		BigInteger[] components = signer.genSignature();

		return new SM2Signature(components[0], components[1], components[2]);

	}

	public static byte[] sm2Signdata(BigInteger privateKey, byte[] transactionHash) throws CryptoException {
		SM2Signer signer = new SM2Signer(new SM3Digest());
		ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(privateKey, Sign.CURVE);
		signer.init(true, privKey);
		signer.update(transactionHash, 0, transactionHash.length);

		byte[] components = signer.generateSignature();

		return components;

	}

	public static boolean sm2Verifydata(BigInteger publicKey, byte[] signdata, byte[] message) throws CryptoException {
		ECPoint ecPoint = Sign.publicPointFromPublic(publicKey);
		ECPublicKeyParameters pubKey = new ECPublicKeyParameters(ecPoint, Sign.CURVE);
		SM2Signer signer = new SM2Signer();
		signer.init(false, pubKey);
		signer.update(message, 0, message.length);

		return signer.verifySignature(signdata);
	}

	public static boolean sm2Verifydata(BigInteger publicKey, SM2Signature signature, byte[] message)
			throws CryptoException {
		ECPoint ecPoint = Sign.publicPointFromPublic(publicKey);
		ECPublicKeyParameters pubKey = new ECPublicKeyParameters(ecPoint, Sign.CURVE);
		SM2Signer signer = new SM2Signer();
		signer.init(false, pubKey);
		signer.update(message, 0, message.length);

		return signer.verifySignature(signature.r, signature.s);
	}

	public static SM2Signature fromSignData(byte[] signData) throws IOException {
		BigInteger[] decoded = STANDARD_DSA_ENCODING.decode(Sign.CURVE.getN(), signData);
		return new SM2Signature(decoded[0], decoded[1], decoded[2]);
	}

	public static byte[] fromSignature(SM2Signature signature) throws IOException {
		return STANDARD_DSA_ENCODING.encode(Sign.CURVE.getN(), signature.r, signature.s, signature.v);
	}

	public static SignatureData getSignData(byte[] signData) throws IOException {
		BigInteger[] decoded = STANDARD_DSA_ENCODING.decode(Sign.CURVE.getN(), signData);
		return new SignatureData(decoded[2].toByteArray(), decoded[0].toByteArray(), decoded[1].toByteArray());
	}

}
